package br.edu.ufcg.unknown.lexico;


import java.io.File;
import java.io.IOException;
import java.util.LinkedList;

import org.apache.commons.collections.CollectionUtils;

import br.edu.ufcg.unknown.exception.EOFUnknownException;
import br.edu.ufcg.unknown.exception.UnknownException;

public class FluxoDeCaractereTest extends UnknownTest {

	private FluxoDeCaractere fluxoDeCaractere;

	public void testLerArquivoInexistente() throws IOException {

		File codigoFonteFile = File.createTempFile("arquivoInexistente", "unknown");
		assertTrue(codigoFonteFile.delete());

		try {
			fluxoDeCaractere = new FluxoDeCaractere(codigoFonteFile);
			fail("Erro! Era pra ser lançada exceção quando tentasse ler um arquivo inexistente!");
		} catch (UnknownException e) {
			// OK
		}

	}

	public void testTemProxCaracter() throws UnknownException {

		fluxoDeCaractere = new FluxoDeCaractere("");
		assertFalse(fluxoDeCaractere.temProxCaractere());

		fluxoDeCaractere = new FluxoDeCaractere("a");
		assertTrue(fluxoDeCaractere.temProxCaractere());

		// " \t\n\r\f"
		fluxoDeCaractere = new FluxoDeCaractere(" ");
		assertTrue(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere = new FluxoDeCaractere("\t");
		assertTrue(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere = new FluxoDeCaractere("\n");
		assertTrue(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere = new FluxoDeCaractere("\r");
		assertTrue(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere = new FluxoDeCaractere("\f");
		assertTrue(fluxoDeCaractere.temProxCaractere());

		fluxoDeCaractere = new FluxoDeCaractere("a");
		fluxoDeCaractere.getProxCaractere();
		fluxoDeCaractere.devolver('a');
		assertTrue(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere.getProxCaractere();
		assertFalse(fluxoDeCaractere.temProxCaractere());

	}
	
	public void testTemProxCaracterComComentario() throws UnknownException {
		//TODO
		fluxoDeCaractere = new FluxoDeCaractere("#asdfasdff");
		assertFalse(fluxoDeCaractere.temProxCaractere());
		fluxoDeCaractere = new FluxoDeCaractere("#asdfasdff\n");
		assertTrue(fluxoDeCaractere.temProxCaractere());
	}

	public void testLerCodigoFonteSimplesSemIgnorarWhitespace() throws UnknownException {
		LinkedList<Character> caracteresEsperados = new LinkedList<Character>();
		String codigoFonte = "a b\nc d\t e\n\r\f";

		for (int i = 0; i < codigoFonte.length(); i++) {
			caracteresEsperados.add(codigoFonte.charAt(i));
		}

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		LinkedList<Character> caracteresRecebidos = new LinkedList<Character>();

		while (fluxoDeCaractere.temProxCaractere()) {
			caracteresRecebidos.add(fluxoDeCaractere.getProxCaractere());
		}

		assertTrue("recebidos: " + toString(caracteresEsperados) + "esperados: " + toString(caracteresRecebidos), CollectionUtils
				.isEqualCollection(caracteresEsperados, caracteresRecebidos));

		codigoFonte = "a";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		fluxoDeCaractere.getProxCaractere();
		fluxoDeCaractere.devolver('a');
		assertTrue(fluxoDeCaractere.temProxCaractere());

	}
	
	public void testLerCodigoFonteSimplesComComentarioSemIgnorarWhitespace() throws UnknownException {
		LinkedList<Character> caracteresEsperados = new LinkedList<Character>();
		String codigoFonte = "a b\nc d\t e\n\r\f";

		for (int i = 0; i < codigoFonte.length(); i++) {
			caracteresEsperados.add(codigoFonte.charAt(i));
		}

		codigoFonte = "a b#aaaaa\nc d\t e#aaaaa\n\r\f#aaaaa"; 
		
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		LinkedList<Character> caracteresRecebidos = new LinkedList<Character>();

		while (fluxoDeCaractere.temProxCaractere()) {
			caracteresRecebidos.add(fluxoDeCaractere.getProxCaractere());
		}

		assertTrue("recebidos: " + toString(caracteresEsperados) + "esperados: " + toString(caracteresRecebidos), CollectionUtils
				.isEqualCollection(caracteresEsperados, caracteresRecebidos));

		codigoFonte = "a";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		fluxoDeCaractere.getProxCaractere();
		fluxoDeCaractere.devolver('a');
		assertTrue(fluxoDeCaractere.temProxCaractere());
	}

	public void testLerCodigoFonteSimplesIgnorandoWhitespace() throws UnknownException {
		LinkedList<Character> caracteresEsperados = new LinkedList<Character>();
		String codigoFonte = "a b\nc d\t e\n\r\f";

		for (int i = 0; i < codigoFonte.length(); i++) {
			if (!Character.isWhitespace(codigoFonte.charAt(i))) {
				caracteresEsperados.add(codigoFonte.charAt(i));
			}
		}

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		LinkedList<Character> caracteresRecebidos = new LinkedList<Character>();

		while (fluxoDeCaractere.temProxCaractereIgWS()) {
			caracteresRecebidos.add(fluxoDeCaractere.getProxCaractereIgWS());
		}

		assertTrue("recebidos: " + toString(caracteresEsperados) + "esperados: " + toString(caracteresRecebidos), CollectionUtils
				.isEqualCollection(caracteresEsperados, caracteresRecebidos));

		codigoFonte = "a";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		fluxoDeCaractere.getProxCaractereIgWS();
		fluxoDeCaractere.devolver('a');
		assertTrue(fluxoDeCaractere.temProxCaractereIgWS());

		codigoFonte = " ";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertFalse(fluxoDeCaractere.temProxCaractereIgWS());

	}
	
	public void testLerCodigoFonteSimplesComComentarioIgnorandoWhitespace() throws UnknownException {
		LinkedList<Character> caracteresEsperados = new LinkedList<Character>();
		String codigoFonte = "a b\nc d\t e\n\r\f";

		for (int i = 0; i < codigoFonte.length(); i++) {
			if (!Character.isWhitespace(codigoFonte.charAt(i))) {
				caracteresEsperados.add(codigoFonte.charAt(i));
			}
		}

		codigoFonte = "a b#aaaaa\nc d\t e#aaaaa\n\r\f#aaaaa";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		LinkedList<Character> caracteresRecebidos = new LinkedList<Character>();

		while (fluxoDeCaractere.temProxCaractereIgWS()) {
			caracteresRecebidos.add(fluxoDeCaractere.getProxCaractereIgWS());
		}

		assertTrue("recebidos: " + toString(caracteresEsperados) + "esperados: " + toString(caracteresRecebidos), CollectionUtils
				.isEqualCollection(caracteresEsperados, caracteresRecebidos));

		codigoFonte = "a";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		fluxoDeCaractere.getProxCaractereIgWS();
		fluxoDeCaractere.devolver('a');
		assertTrue(fluxoDeCaractere.temProxCaractereIgWS());

		codigoFonte = " ";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertFalse(fluxoDeCaractere.temProxCaractereIgWS());
	}

	public void testLerCaracterEDevolverSemIgnorarWhitespace() throws UnknownException {
		String codigoFonte = "ab cdef";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(' ', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals('c', fluxoDeCaractere.getProxCaractere().charValue());
		fluxoDeCaractere.devolver('c');
		fluxoDeCaractere.devolver(' ');
		assertEquals(' ', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals('c', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals('d', fluxoDeCaractere.getProxCaractere().charValue());

	}

	public void testLerCaracterEDevolverIgnorandoWhitespace() throws UnknownException {

		String codigoFonte = "ab cdef";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals('a', fluxoDeCaractere.getProxCaractereIgWS().charValue());
		assertEquals('b', fluxoDeCaractere.getProxCaractereIgWS().charValue());
		assertEquals('c', fluxoDeCaractere.getProxCaractereIgWS().charValue());
		fluxoDeCaractere.devolver('c');
		fluxoDeCaractere.devolver(' ');
		assertEquals('c', fluxoDeCaractere.getProxCaractereIgWS().charValue());
		assertEquals('d', fluxoDeCaractere.getProxCaractereIgWS().charValue());
	}

	public void testLerCaractereEDevolverUmCaractereInvalido() throws UnknownException {

		String codigoFonte = "ab";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals('a', fluxoDeCaractere.getProxCaractereIgWS().charValue());
		assertEquals('b', fluxoDeCaractere.getProxCaractereIgWS().charValue());

		boolean caractereDevolvido;

		caractereDevolvido = fluxoDeCaractere.devolver('?');
		assertTrue(caractereDevolvido);// FluxoDeCaractere não controla quem é
		// caractere válido ou inválido
	}

	public void testLeituraDeCaracteresValidos() {

		StringBuilder sb = new StringBuilder();

		for (char caractere = 'a'; caractere < 'z'; caractere++) {
			sb.append(caractere);
		}
		for (char caractere = 'A'; caractere < 'Z'; caractere++) {
			sb.append(caractere);
		}
		for (char caractere = '0'; caractere < '9'; caractere++) {
			sb.append(caractere);
		}
		sb.append("+");
		sb.append("-");
		sb.append("*");
		sb.append("/");
		sb.append("=");
		sb.append(":=");
		sb.append(":");
		sb.append(">");
		sb.append("<");
		sb.append(">=");
		sb.append("<=");
		sb.append("<>");
		sb.append(")");
		sb.append("(");
		sb.append("]");
		sb.append("[");
		sb.append(";");
		sb.append("'");
		sb.append(" ");

		String codigoFonte = sb.toString();

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		while (fluxoDeCaractere.temProxCaractere()) {
			fluxoDeCaractere.getProxCaractere();
		}
	}

	public void testLeituraComFluxoVazio() throws UnknownException {

		String codigoFonte = "ab";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());

		try {
			fluxoDeCaractere.getProxCaractere();
			fail("Erro! Era pra ser lançada uma exceção por tentar recuperar um caractere de um fluxo vazio!");
		} catch (EOFUnknownException e) {
			// OK
		}

	}

	public void testGetLinhaUnicaLinha() throws UnknownException {
		String codigoFonte = "ab";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(1, fluxoDeCaractere.getLinha());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(1, fluxoDeCaractere.getLinha());
	}

	public void testGetLinhaMaisDeUmaLinha() throws UnknownException {
		String codigoFonte = "ab\ncd";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(1, fluxoDeCaractere.getLinha());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'd'
		assertEquals(2, fluxoDeCaractere.getLinha());
	}

	public void testGetLinhaMaisDeUmaLinhaComRetrocesso() throws UnknownException {
		String codigoFonte = "ab\ncd";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(1, fluxoDeCaractere.getLinha());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(2, fluxoDeCaractere.getLinha());

		fluxoDeCaractere.devolver('c');
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.devolver('\n');
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.devolver('b');
		assertEquals(1, fluxoDeCaractere.getLinha());

		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(1, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(2, fluxoDeCaractere.getLinha());
		fluxoDeCaractere.getProxCaractere();// lido 'd'
		assertEquals(2, fluxoDeCaractere.getLinha());
	}

	public void testGetColunaUnicaLinha() throws UnknownException {
		String codigoFonte = "ab";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(0, fluxoDeCaractere.getColuna());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(2, fluxoDeCaractere.getColuna());
	}

	public void testGetColunaMaisDeUmaLinha() throws UnknownException {
		String codigoFonte = "ab\ncd";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(0, fluxoDeCaractere.getColuna());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(2, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(0, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(1, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'd'
		assertEquals(2, fluxoDeCaractere.getColuna());
	}

	public void testGetColunaMaisDeUmaLinhaComRetrocesso() throws UnknownException {
		String codigoFonte = "ab\ncd";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);
		assertEquals(0, fluxoDeCaractere.getColuna());

		fluxoDeCaractere.getProxCaractere();// lido 'a'
		assertEquals(1, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(2, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(0, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(1, fluxoDeCaractere.getColuna());

		fluxoDeCaractere.devolver('c');
		assertEquals(0, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.devolver('\n');
		assertEquals(2, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.devolver('b');
		assertEquals(1, fluxoDeCaractere.getColuna());

		fluxoDeCaractere.getProxCaractere();// lido 'b'
		assertEquals(2, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido '\n'
		assertEquals(0, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'c'
		assertEquals(1, fluxoDeCaractere.getColuna());
		fluxoDeCaractere.getProxCaractere();// lido 'd'
		assertEquals(2, fluxoDeCaractere.getColuna());
	}

	public void testGetLinhaProxCaractereIgWS() {
		String codigoFonte = "abc\n";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getLinhaProxCaractereIgWS());

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('c', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(-1, fluxoDeCaractere.getLinhaProxCaractereIgWS());

		codigoFonte = "a\nb";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(-1, fluxoDeCaractere.getLinhaProxCaractereIgWS());

		codigoFonte = "a\nb\n";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(3, fluxoDeCaractere.getLinhaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(-1, fluxoDeCaractere.getLinhaProxCaractereIgWS());

	}

	public void testGetColunaProxCaractereIgWS() {
		String codigoFonte = "abc\n";

		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(2, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(3, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('c', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(0, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(-1, fluxoDeCaractere.getColunaProxCaractereIgWS());

		codigoFonte = "a\nb";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());

		codigoFonte = "a\nb\n";
		fluxoDeCaractere = new FluxoDeCaractere(codigoFonte);

		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());

		assertEquals('a', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('\n', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(1, fluxoDeCaractere.getColunaProxCaractereIgWS());
		assertEquals('b', fluxoDeCaractere.getProxCaractere().charValue());
		assertEquals(0, fluxoDeCaractere.getColunaProxCaractereIgWS());

	}

}
